﻿
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;

namespace hamster
{
    class Wheel
    {
        public const int CIRCLE_BITS = 150;
        public const float RADIUS_IN = 7.5f;
        public const float RADIUS_OUT = 9f;
        public const float DEPTH = -1.5f;

        VertexPositionNormalTexture[] _wheel;
        VertexPositionNormalTexture[] _grate;
        VertexPositionNormalTexture[] _bloom;
        Effect _effect;
        Texture2D _wheelTexture;
        Texture2D _grateTexture;
        Texture2D _bloomTexture; 
        float _angleInRadians;
        float _spinSpeed;

        public void Create(ContentManager content)
        {
            Reload(content);
        }

        public void Reload(ContentManager content)
        {
            if (_effect != null) _effect.Dispose();
            if (_wheelTexture != null) _wheelTexture.Dispose();
            if (_grateTexture != null) _grateTexture.Dispose();
            if (_bloomTexture != null) _bloomTexture.Dispose();

            _effect = content.Load<Effect>("Shader");
            _wheelTexture = content.Load<Texture2D>("wheel");
            _grateTexture = content.Load<Texture2D>("grate");
            _bloomTexture = content.Load<Texture2D>("bloom");

            _wheel = ComputeWheelGeometry();
            _grate = ComputeGrateGeometry();
        }

        public VertexPositionNormalTexture[] ComputeWheelGeometry()
        {
            int bits = CIRCLE_BITS;
            VertexPositionNormalTexture[] wheel = new VertexPositionNormalTexture[2 * 6 * bits];
            for (int i = 0; i < bits; i++)
            {
                float frac_xn = ((float)i / (float)bits);
                float frac_xm = ((float)(1 + i) / (float)bits);
                float frac_n = 2 * MathHelper.Pi * frac_xn;
                float frac_m = 2 * MathHelper.Pi * frac_xm;
                float y_n = (float)Math.Sin(frac_n);
                float x_n = (float)Math.Cos(frac_n);
                float y_m = (float)Math.Sin(frac_m);
                float x_m = (float)Math.Cos(frac_m);

                float radius_slat = (RADIUS_IN + RADIUS_OUT) * 0.5f;


                //frac_xn *= .02f;
                float uv_s = 4.15f;
                ///////////////
                wheel[i * 12 + 0] = new VertexPositionNormalTexture
                {
                    Position = new Vector3(x_n * RADIUS_IN, y_n * RADIUS_IN, 0f),
                    TextureCoordinate = new Vector2(frac_xn * uv_s, .5f)
                };

                wheel[i * 12 + 1] = new VertexPositionNormalTexture
                {
                    Position = new Vector3(x_m * RADIUS_IN, y_m * RADIUS_IN, 0f),
                    TextureCoordinate = new Vector2(frac_xm * uv_s, .5f)
                };

                wheel[i * 12 + 2] = new VertexPositionNormalTexture
                {
                    Position = new Vector3(x_m * RADIUS_OUT, y_m * RADIUS_OUT, 0f),
                    TextureCoordinate = new Vector2(frac_xm * uv_s, 0)
                };
                ////
                wheel[i * 12 + 3] = new VertexPositionNormalTexture
                {
                    Position = new Vector3(x_m * RADIUS_OUT, y_m * RADIUS_OUT, 0f),
                    TextureCoordinate = new Vector2(frac_xm * uv_s, 0)
                };

                wheel[i * 12 + 4] = new VertexPositionNormalTexture
                {
                    Position = new Vector3(x_n * RADIUS_OUT, y_n * RADIUS_OUT, 0f),
                    TextureCoordinate = new Vector2(frac_xn * uv_s, 0)
                };

                wheel[i * 12 + 5] = new VertexPositionNormalTexture
                {
                    Position = new Vector3(x_n * RADIUS_IN, y_n * RADIUS_IN, 0f),
                    TextureCoordinate = new Vector2(frac_xn * uv_s, .5f)
                };
                /////////////// slats
                wheel[i * 12 + 6] = new VertexPositionNormalTexture
                {
                    Position = new Vector3(x_n * radius_slat, y_n * radius_slat, DEPTH),
                    TextureCoordinate = new Vector2(frac_xn * uv_s, 0.5f)
                };

                wheel[i * 12 + 7] = new VertexPositionNormalTexture
                {
                    Position = new Vector3(x_m * radius_slat, y_m * radius_slat, DEPTH),
                    TextureCoordinate = new Vector2(frac_xm * uv_s, 0.5f)
                };

                wheel[i * 12 + 8] = new VertexPositionNormalTexture
                {
                    Position = new Vector3(x_m * radius_slat, y_m * radius_slat, 0f),
                    TextureCoordinate = new Vector2(frac_xm * uv_s, 1)
                };
                ////
                wheel[i * 12 + 9] = new VertexPositionNormalTexture
                {
                    Position = new Vector3(x_m * radius_slat, y_m * radius_slat, 0f),
                    TextureCoordinate = new Vector2(frac_xm * uv_s, 1)
                };

                wheel[i * 12 + 10] = new VertexPositionNormalTexture
                {
                    Position = new Vector3(x_n * radius_slat, y_n * radius_slat, 0f),
                    TextureCoordinate = new Vector2(frac_xn * uv_s, 1)
                };

                wheel[i * 12 + 11] = new VertexPositionNormalTexture
                {
                    Position = new Vector3(x_n * radius_slat, y_n * radius_slat, DEPTH),
                    TextureCoordinate = new Vector2(frac_xn * uv_s, 0.5f)
                };


            }

            return wheel;
        }

        public VertexPositionNormalTexture[] ComputeGrateGeometry()
        {
            int bits = 5;

            float radius = 5f;
            float grate_in = 2;
            VertexPositionNormalTexture[] wheel = new VertexPositionNormalTexture[1 * 6 * bits];
            for (int i = 0; i < bits; i++)
            {
                float frac_xn = ((float)i / (float)bits);
                float frac_n = 2 * MathHelper.Pi * frac_xn;

                float y_n = (float)Math.Sin(frac_n);
                float x_n = (float)Math.Cos(frac_n);

                Vector3 perp = new Vector3(-y_n, x_n, 0f) * 0.75f;
                Vector3 range = new Vector3(x_n, y_n, 0f) * 8f;
                Vector3 range_in = new Vector3(x_n, y_n, 0f) * 2f;
                Vector3 nudge_in = new Vector3(0, 0, -1);
                //frac_xn *= .02f;
                float uv_s = 1f;
                ///////////////
                wheel[i * 6 + 0] = new VertexPositionNormalTexture
                {
                    Position = -perp + range_in,
                    TextureCoordinate = new Vector2(0, -1)
                };

                wheel[i * 6 + 1] = new VertexPositionNormalTexture
                {
                    Position = perp + range_in,
                    TextureCoordinate = new Vector2(1, -1)
                };

                wheel[i * 6 + 2] = new VertexPositionNormalTexture
                {
                    Position = perp + range + nudge_in,
                    TextureCoordinate = new Vector2(1, 0)
                };
                ////
                wheel[i * 6 + 3] = new VertexPositionNormalTexture
                {
                    Position = perp + range + nudge_in,
                    TextureCoordinate = new Vector2(1, 0)
                };

                wheel[i * 6 + 4] = new VertexPositionNormalTexture
                {
                    Position = -perp + range + nudge_in,
                    TextureCoordinate = new Vector2(0, 0)
                };

                wheel[i * 6 + 5] = new VertexPositionNormalTexture
                {
                    Position = -perp + range_in,
                    TextureCoordinate = new Vector2(0, -1)
                };


            }

            return wheel;
        }

        public VertexPositionNormalTexture[] ComputeBloomGeometry()
        {
            int bits = 5;
            float ind = 5.65f;
            float outd = 7.5f; ;
            VertexPositionNormalTexture[] wheel = new VertexPositionNormalTexture[1 * 6 * bits];
            for (int i = 0; i < bits; i++)
            {
                float frac_xn = ((float)i / (float)bits);
                float frac_n = 2 * MathHelper.Pi * frac_xn;

                float y_n = (float)Math.Sin(frac_n);
                float x_n = (float)Math.Cos(frac_n);

                Vector3 perp = new Vector3(-y_n, x_n, 0f) * 0.75f;
                Vector3 range = new Vector3(x_n, y_n, 0f) * outd;
                Vector3 range_in = new Vector3(x_n, y_n, 0f) * ind;
                Vector3 nudge_in = new Vector3(0, 0, -1.0f);
                //frac_xn *= .02f;
                float uv_s = 1f;
                ///////////////
                wheel[i * 6 + 0] = new VertexPositionNormalTexture
                {
                    Position = -perp + range_in + nudge_in,
                    TextureCoordinate = new Vector2(0, -1)
                };

                wheel[i * 6 + 1] = new VertexPositionNormalTexture
                {
                    Position = perp + range_in + nudge_in,
                    TextureCoordinate = new Vector2(1, -1)
                };

                wheel[i * 6 + 2] = new VertexPositionNormalTexture
                {
                    Position = perp + range + nudge_in,
                    TextureCoordinate = new Vector2(1, 0)
                };
                ////
                wheel[i * 6 + 3] = new VertexPositionNormalTexture
                {
                    Position = perp + range + nudge_in,
                    TextureCoordinate = new Vector2(1, 0)
                };

                wheel[i * 6 + 4] = new VertexPositionNormalTexture
                {
                    Position = -perp + range + nudge_in,
                    TextureCoordinate = new Vector2(0, 0)
                };

                wheel[i * 6 + 5] = new VertexPositionNormalTexture
                {
                    Position = -perp + range_in + nudge_in,
                    TextureCoordinate = new Vector2(0, -1)
                };


            }

            return wheel;
        }

        public void Update(float dt)
        {
            //_angleInRadians += dt * _spinSpeed * 0.1f;
        }

        

        public void Draw(GraphicsDevice device)
        {
            
            _effect.Parameters["World"].SetValue(Matrix.CreateRotationZ(_angleInRadians));
            _effect.Parameters["View"].SetValue(Game1.viewMatrix);
            _effect.Parameters["Projection"].SetValue(Game1.projectionMatrix);
            _effect.Begin();

            _effect.CommitChanges();
            _effect.Parameters["UserTexture"].SetValue(_wheelTexture);
            _effect.CurrentTechnique.Passes[0].Begin();
            device.DrawUserPrimitives<VertexPositionNormalTexture>(
                PrimitiveType.TriangleList,
                _wheel,
                0,
                _wheel.Length / 3
            );
            _effect.CurrentTechnique.Passes[0].End();

            _effect.End();
            

            _grate = ComputeGrateGeometry();
        }


        public void DrawGrate(GraphicsDevice device)
        {
            
            device.RenderState.DepthBufferWriteEnable = false;
            device.RenderState.DepthBufferEnable = false;
            device.RenderState.AlphaBlendEnable = true;
            device.RenderState.SourceBlend = Blend.SourceAlpha;
            device.RenderState.DestinationBlend = Blend.InverseSourceAlpha;

            _effect.Parameters["World"].SetValue(Matrix.CreateRotationZ(_angleInRadians));
            _effect.Parameters["View"].SetValue(Game1.viewMatrix);
            _effect.Parameters["Projection"].SetValue(Game1.projectionMatrix);
            _effect.Begin();

            _effect.CommitChanges();
            _effect.Parameters["UserTexture"].SetValue(_grateTexture);
            _effect.CurrentTechnique.Passes[0].Begin();
            device.DrawUserPrimitives<VertexPositionNormalTexture>(
                PrimitiveType.TriangleList,
                _grate,
                0,
                _grate.Length / 3
            );
            _effect.CurrentTechnique.Passes[0].End();
            _effect.End();

            device.RenderState.AlphaBlendEnable = false;


            _grate = ComputeGrateGeometry();
            device.RenderState.DepthBufferWriteEnable = true;
            device.RenderState.DepthBufferEnable = true;
              
            DrawBloom(device);
        }
        float d = 0;

        public void DrawBloom(GraphicsDevice device)
        {
            _bloom = ComputeBloomGeometry();
            device.RenderState.DepthBufferWriteEnable = false;
            device.RenderState.DepthBufferEnable = false;
            device.RenderState.AlphaBlendEnable = true;
            device.RenderState.AlphaSourceBlend = Blend.One;
            device.RenderState.AlphaDestinationBlend = Blend.InverseSourceAlpha;
            device.RenderState.SourceBlend = Blend.SourceAlpha;
            device.RenderState.DestinationBlend = Blend.One;
            device.RenderState.BlendFunction = BlendFunction.Add;

            _effect.Parameters["World"].SetValue(Matrix.CreateRotationZ(_angleInRadians));
            _effect.Parameters["View"].SetValue(Game1.viewMatrix);
            _effect.Parameters["Projection"].SetValue(Game1.projectionMatrix);
            _effect.Begin();

            if (Game1.RandomFloat < 0.1f)
            {
                d = 0.5f + Game1.RandomFloat;
            }
            else
            {
                d -= 0.05f;
            }

            _effect.Parameters["UserTexture"].SetValue(_bloomTexture);
            _effect.Parameters["Time"].SetValue(d);
            _effect.CommitChanges();
            _effect.CurrentTechnique.Passes[1].Begin();
            device.DrawUserPrimitives<VertexPositionNormalTexture>(
                PrimitiveType.TriangleList,
                _bloom,
                0,
                _bloom.Length / 3
            );
            _effect.CurrentTechnique.Passes[1].End();
            _effect.End();

            device.RenderState.AlphaBlendEnable = false;


            _grate = ComputeGrateGeometry();
            device.RenderState.DepthBufferWriteEnable = true;
            device.RenderState.DepthBufferEnable = true;
        }
    }
}
